package com.ohos.connect.devicecloud.iot.modular.device.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.ohos.connect.devicecloud.app.api.ProjectApi;
import com.ohos.connect.devicecloud.app.api.ProjectDeviceApi;
import com.ohos.connect.devicecloud.app.model.vo.AppProjectVO;
import com.ohos.connect.devicecloud.auth.core.util.StpAppLoginUserUtil;
import com.ohos.connect.devicecloud.iot.model.dto.*;
import com.ohos.connect.devicecloud.iot.model.dto.deviceactive.DeviceActiveDTO;
import com.ohos.connect.devicecloud.iot.model.dto.deviceinfosync.DeviceInfoSyncDTO;
import com.ohos.connect.devicecloud.iot.model.vo.*;
import com.ohos.connect.devicecloud.iot.model.vo.deviceactive.DeviceActiveLicenseInfoVO;
import com.ohos.connect.devicecloud.iot.model.vo.deviceactive.DeviceActiveVO;
import com.ohos.connect.devicecloud.iot.modular.device.constant.DeviceConstant;
import com.ohos.connect.devicecloud.iot.modular.device.model.dto.*;
import com.ohos.connect.devicecloud.iot.modular.device.model.vo.DeviceRegisterCodeVO;
import com.ohos.connect.devicecloud.iot.modular.device.model.vo.DeviceRegisterInfoVO;
import com.ohos.connect.devicecloud.iot.modular.device.model.vo.shadowdata.DeviceShadowDataVO;
import com.ohos.connect.devicecloud.iot.modular.device.model.vo.shadowdata.DeviceShadowDeviceInfoVO;
import com.ohos.connect.devicecloud.iot.util.PBKDF2Util;
import com.ohos.connect.devicecloud.mqtt.api.CoapApi;
import com.ohos.connect.devicecloud.mqtt.api.MqttApi;
import com.ohos.connect.devicecloud.mqtt.core.model.dto.CoapToHttpDTO;
import com.ohos.connect.devicecloud.mqtt.core.model.vo.CoapToHttpVO;
import org.eclipse.paho.mqttv5.common.MqttException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ohos.connect.devicecloud.common.exception.CommonException;
import com.ohos.connect.devicecloud.iot.modular.device.model.entity.Device;
import com.ohos.connect.devicecloud.iot.modular.device.mapper.DeviceMapper;
import com.ohos.connect.devicecloud.iot.modular.device.model.param.DeviceAddParam;
import com.ohos.connect.devicecloud.iot.modular.device.model.param.DeviceEditParam;
import com.ohos.connect.devicecloud.iot.modular.device.model.param.DeviceIdParam;
import com.ohos.connect.devicecloud.iot.modular.device.service.DeviceService;
import java.security.SecureRandom;

import javax.annotation.Resource;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.eclipse.paho.mqttv5.common.MqttMessage;

/**
 * 设备表Service接口实现类
 *
 * @author Dongtao
 * @date  2024/12/17 10:43
 **/
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {

    @Resource
    private ProjectApi projectApi;

    @Resource
    private ProjectDeviceApi projectDeviceApi;

    @Resource
    private CoapApi coapApi;

    @Resource
    private MqttApi mqttApi;

    @Value("${iot.cloudAddress}")
    private String CLOUD_ADDRESS = "connect.harmonytsc.com:7881";

    @Value("${iot.activeTime}")
    private Integer ACTIVE_TIME = 300;

    @Value("${iot.redis.prefix}")
    private String IOT_REDIS_PREFIX = "iot";

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    @Override
    public DeviceRegisterCodeVO getRegisterAuthCode(DeviceRegisterCodeDTO deviceRegisterCodeDTO) {
        DeviceRegisterCodeVO deviceRegisterCodeVO = new DeviceRegisterCodeVO();

        // 校验projectId
        checkProjectId(deviceRegisterCodeDTO.getProjectId());

        // todo:结合场景业务云，校验productId
        if(ObjectUtil.isNull(deviceRegisterCodeDTO.getProductId())){
            throw new CommonException("产品id不能为空");
        }

        // 生成deviceId
        String deviceId = UUID.randomUUID().toString();
        // 生成设备注册验证码，并设置过期时间
        String registerAuthCode = RandomUtil.randomNumbers(8);
        // 生成psk
        String psk = getPsk();

        // 写入redis,设置验证码过期时间
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("registerAuthCode",registerAuthCode);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("psk",psk);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("projectId",deviceRegisterCodeDTO.getProjectId());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("productId",deviceRegisterCodeDTO.getProductId());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("registryType",deviceRegisterCodeDTO.getRegistryType());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("sn",deviceRegisterCodeDTO.getSn());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("uniqueDeviceId","".equals(deviceRegisterCodeDTO.getSn()) ? "false" : "true");
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("userId",StpAppLoginUserUtil.getAppLoginUserId());

        // 设置key过期时间
        redisTemplate.expire(IOT_REDIS_PREFIX+deviceId,ACTIVE_TIME, TimeUnit.SECONDS);

        deviceRegisterCodeVO.setDeviceId(deviceId);
        deviceRegisterCodeVO.setVerifyCode(registerAuthCode);
        deviceRegisterCodeVO.setPsk(psk);
        deviceRegisterCodeVO.setCloudAddress(CLOUD_ADDRESS);
        deviceRegisterCodeVO.setActiveTime(ACTIVE_TIME);
        deviceRegisterCodeVO.setActivated(false);

        return deviceRegisterCodeVO;
    }

    // 随机生成字符串
    private String getPsk(){
        // 生成一个16字节的随机字节数组
        byte[] randomBytes = RandomUtil.randomBytes(16);

        // 将字节数组转换为十六进制字符串
        StringBuilder hexString = new StringBuilder();
        for (byte b : randomBytes) {
            // 将每个字节转换为两位十六进制数，并追加到StringBuilder中
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0'); // 如果只有一个字符，前面补0
            }
            hexString.append(hex);
        }

        // 输出生成的十六进制字符串
        System.out.println("PSK =====> " + hexString.toString());
        return hexString.toString();
    }


    @Override
    public Boolean getDeviceActiveStatus(String projectId, String devId) {
        if(ObjectUtil.isNull(projectId)){
            throw new CommonException("项目id不能为空");
        }

        if(ObjectUtil.isNull(devId)){
            throw new CommonException("设备id不能为空");
        }
        checkProjectId(projectId);

        Device device = this.getById(devId);
        if(ObjectUtil.isNull(device)){
            throw new CommonException("设备不存在");
        }

        if(ObjectUtil.isNull(device.getActivated()) || !device.getActivated()){
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public DeviceRegisterInfoVO appRegisterDevice(DeviceRegisterInfoInsertDTO deviceRegisterInfoInsertDTO) {
        DeviceRegisterInfoVO deviceRegisterInfoVO = new DeviceRegisterInfoVO();
        checkProjectId(deviceRegisterInfoInsertDTO.getProjectId());

        AppProxyDeviceInsertDTO deviceInsertDTO = deviceRegisterInfoInsertDTO.getDevInfo();

        // 生成deviceId
        String deviceId = UUID.randomUUID().toString();

        Device device = BeanUtil.copyProperties(deviceInsertDTO,Device.class);
        device.setId(deviceId);
        device.setDeviceType(deviceInsertDTO.getDevType());
        device.setProductId(deviceInsertDTO.getProdId());
        device.setModelData(JSON.toJSONString(deviceRegisterInfoInsertDTO.getServices()));
        device.setPassword("123456");
        // 存储设备信息到Redis


        // 持久化存储设备信息到数据库
        boolean saveDevice = this.save(device);
        if(Boolean.FALSE.equals(saveDevice)){
            throw new CommonException("保存设备信息失败");
        }


        deviceRegisterInfoVO.setDevId(deviceId);

        return deviceRegisterInfoVO;
    }

    @Transactional
    @Override
    public Boolean changeDeviceName(String projectId, String deviceId ,DeviceNameUpdateDTO deviceNameUpdateDTO) {
        checkProjectId(projectId);

        if(ObjectUtil.isNull(deviceId)){
            throw new CommonException("设备id不能为空");
        }
        Device device = this.getById(deviceId);
        if(ObjectUtil.isNull(device)){
            throw new CommonException("未查询到设备");
        }

        device.setName(deviceNameUpdateDTO.getName());
        return this.saveOrUpdate(device);
    }

    @Override
    public Boolean deviceControl(DeviceControlDTO deviceControlDTO) {
//        JSONObject servicesObj = deviceControlDTO.getServices();


        List<JSONObject> serviceList =  (List<JSONObject>)deviceControlDTO.getServices();
        for(int i=0 ; i<serviceList.size(); i++ ){
            JSONObject obj = new JSONObject(serviceList.get(i));
            CoapToHttpDTO coapToHttpDTO = new CoapToHttpDTO();
            coapToHttpDTO.setUrl("/"+obj.get("sid"));
            coapToHttpDTO.setMethod("POST");
            coapToHttpDTO.setBody(obj.get("data").toString());

            HashMap<String,Object> option = new HashMap<>();
            option.put("REQ_ID",RandomUtil.randomString(10));
            option.put("DEV_ID",deviceControlDTO.getDevId());
            option.put("USER_ID", StpAppLoginUserUtil.getAppLoginUserId());
            option.put("SEQ_NUM_ID",RandomUtil.randomNumbers(5));
            coapToHttpDTO.setOption(option);

            CoapToHttpVO coapToHttpVO = coapApi.sendCoapMessage(coapToHttpDTO);
        }

        return Boolean.TRUE;
    }

    @Override
    public List<DeviceAuthCodeVO> getDeviceAuthCodeList(List<DeviceAuthCodeDTO> deviceAuthCodeDTOList) {
        List<DeviceAuthCodeVO> deviceAuthCodeVOList = new ArrayList<>();
        for (DeviceAuthCodeDTO deviceAuthCodeDTO : deviceAuthCodeDTOList) {
            DeviceAuthCodeVO deviceAuthCodeVO = new DeviceAuthCodeVO();
            deviceAuthCodeVO = this.deviceAuthCode(deviceAuthCodeDTO);
            deviceAuthCodeVOList.add(deviceAuthCodeVO);
        }
        return deviceAuthCodeVOList;
    }

    @Override
    public DeviceShadowDataVO getDeviceShadowData(DeviceShadeDataGetDTO deviceShadeDataGetDTO) {
        DeviceShadowDataVO deviceShadowDataVO = new DeviceShadowDataVO();
        String deviceId = deviceShadeDataGetDTO.getDevid();
        checkProjectId(deviceShadeDataGetDTO.getProjectId());

        if(Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))){
            throw new CommonException("设备不存在，设备id: " + deviceId);
        }

        JSONObject modelData = (JSONObject)redisTemplate.boundHashOps(IOT_REDIS_PREFIX +  deviceId).get("modelData");

        Device device = this.getById(deviceId);
        DeviceShadowDeviceInfoVO deviceInfoVO = BeanUtil.copyProperties(device, DeviceShadowDeviceInfoVO.class);

        deviceShadowDataVO.setDevId(deviceId);
        deviceShadowDataVO.setGatewayId("");
        deviceShadowDataVO.setNoteType("noteType");
        deviceShadowDataVO.setServices(modelData);
        deviceShadowDataVO.setDevInfo(deviceInfoVO);

        return deviceShadowDataVO;
    }

    @Override
    public DevicePskVO exchangeDeviceSn(DevicePskDTO devicePskDTO){
        String deviceId = devicePskDTO.getDevId();
        // 校验设备id，设备注册验证码是否失效
        if(Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))){
            throw new CommonException("设备id错误或者设备注册验证码失效");
        }

        // 获取psk
        String psk = Objects.requireNonNull(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("psk")).toString();

        // 随机生成sn2
        String sn2 = getRandomSn();

        // 根据psk、sn1、sn2，结合PBKDF2算法算出加密秘钥
        byte[] aesSecret = new byte[32];
        try{
            aesSecret = PBKDF2Util.pbkdf2(psk,(devicePskDTO.getSn1()+sn2).getBytes(), DeviceConstant.PBKDF2_ITERATION,DeviceConstant.PBKDF2_SALT_LENGTH);
        }catch (NoSuchAlgorithmException | InvalidKeySpecException e){
            e.printStackTrace();
        }
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX+deviceId).put("aesSecret", HexUtil.encodeHexStr(aesSecret));

        DevicePskVO devicePskVO = new DevicePskVO();
        devicePskVO.setSn2(sn2);
        devicePskVO.setSeq(devicePskDTO.getSeq()+1);
        devicePskVO.setEncryptMode(DeviceConstant.PLAIN_TEXT);
        devicePskVO.setErrcode(0);

        return devicePskVO;
    }

    private String getRandomSn(){
        // 生成8字节的随机数据
        byte[] randomBytes = RandomUtil.randomBytes(8);

        // 将字节数组转换成十六进制字符串
        // Hutool没有直接提供字节到十六进制字符串的转换方法，所以我们使用Java标准库
        StringBuilder hexString = new StringBuilder(2 * randomBytes.length); // 每个字节变成两个十六进制字符
        for (byte b : randomBytes) {
            // 将每个字节转换成两位的十六进制数并追加到StringBuilder中
            hexString.append(String.format("%02x", b & 0xff));
        }

        return hexString.toString();
    }

    @Transactional
    @Override
    public DeviceActiveVO activeDevice(DeviceActiveDTO deviceActiveDTO) {
        DeviceActiveVO deviceActiveVO = new DeviceActiveVO();

        // 判断注册码是否过期
        String deviceId = deviceActiveDTO.getDevId();
        if(Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))){
//            throw new CommonException("设备id错误或者设备注册验证码失效");
            deviceActiveVO.setErrcode(1);
            return deviceActiveVO;
        }

        // 校验注册码
        String rightCode = Objects.requireNonNull(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("registerAuthCode")).toString();
        if(!rightCode.equals(deviceActiveDTO.getCode())){
//            throw new CommonException("设备注册验证码错误");
            deviceActiveVO.setErrcode(2);
            return deviceActiveVO;
        }

        // 判断productId是否吻合
        String rightProductId = Objects.requireNonNull(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("productId")).toString();
        if(!rightProductId.equals(deviceActiveDTO.getDevInfo().getProdId())){
//            throw new CommonException("设备产品不匹配");
            deviceActiveVO.setErrcode(3);
            return deviceActiveVO;
        }

        // 判断SN是否需要校验
        if(Objects.equals(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("uniqueDeviceId"), true)){
            if(!Objects.equals(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("sn"), deviceActiveDTO.getDevInfo().getSn())){
//                throw new CommonException("设备Sn不匹配");
                deviceActiveVO.setErrcode(4);
                return deviceActiveVO;
            }
        }

        // 校验sn是否已被注册
        if(isDeviceSnExist(deviceActiveDTO.getDevInfo().getSn())){
//                throw new CommonException("设备已被注册");
            deviceActiveVO.setErrcode(4);
            return deviceActiveVO;
        }

        // 校验设备ID在设备表中是否已存在
        if(isDeviceIdExist(deviceId)){
//            throw new CommonException("设备ID已存在，请勿重复激活");
            deviceActiveVO.setErrcode(5);
            return deviceActiveVO;
        }



        // 将Redis中的设备信息持久化
        Boolean persistDeviceInfo = redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).persist();
        if(Boolean.FALSE.equals(persistDeviceInfo)){
//            throw new CommonException("Redis持久化该设备信息失败，deviceId : " + deviceId);
            deviceActiveVO.setErrcode(6);
            return deviceActiveVO;
        }

        // 生成设备密码
        String password = RandomUtil.randomString(32);
        String licenseId = UUID.randomUUID().toString();
        String license = RandomUtil.randomNumbers(32);

        // 插入到设备表
        Device device = BeanUtil.toBean(deviceActiveDTO.getDevInfo(), Device.class);
        device.setProductId(deviceActiveDTO.getDevInfo().getProdId());
        device.setId(deviceId);
        device.setPassword(password);
        device.setLicenseId(licenseId);
        device.setLicense(license);
        device.setActivated(true);
        boolean addResult = this.save(device);
        if(!addResult){
            throw new CommonException("保存设备激活信息失败");
        }

        // 插入项目设备表
        projectDeviceApi.insertProjectDevice(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("projectId").toString(),deviceId);


        deviceActiveNotice(deviceId,device);

        deviceActiveVO.setErrcode(0);
        deviceActiveVO.setDevId(device.getId());
        deviceActiveVO.setSecret(device.getPassword());

        DeviceActiveLicenseInfoVO deviceActiveLicenseInfoVO = new DeviceActiveLicenseInfoVO();
        deviceActiveLicenseInfoVO.setUuid(licenseId);
        deviceActiveLicenseInfoVO.setLicense(license);

        deviceActiveVO.setLicenseInfo(deviceActiveLicenseInfoVO);

        return deviceActiveVO;
    }

    // todo:向APP发送设备激活MQTT通知
    private void deviceActiveNotice(String devId,Device device){
        String topic = "/user/"+ getDeviceUserAccount(devId) + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "bindDevice");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("resultCode", "succeeded");
        body.put("devId", devId);
        body.put("verifyCodeDevId", "");

        JSONObject devObj = new JSONObject();

        devObj.put("sn", device.getSn() );
        devObj.put("model", device.getModel() );
        devObj.put("devType", device.getDeviceType() );
        devObj.put("manu", device.getManu() );
        devObj.put("mac", device.getMac() );
        devObj.put("hiv", device.getHiv() );
        devObj.put("fwv", device.getFwv() );
        devObj.put("hwv", device.getHwv() );
        devObj.put("swv", device.getSwv() );
        devObj.put("protType", "" );
        devObj.put("prodId", device.getProductId() );

        body.put("devInfo", "devObj");

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        try {
            mqttApi.sendMqttMessage(topic,message);
        }catch (MqttException e){
            e.printStackTrace();
        }


    }

    @Override
    public DeviceLoginVO loginDevice(DeviceLoginDTO deviceLoginDTO) {
        DeviceLoginVO deviceLoginVO = new DeviceLoginVO();

        String deviceId = deviceLoginDTO.getDevId();

        Device device = this.getById(deviceId);

        // 判断设备是否存在
        if(ObjectUtil.isNull(device)){
//            throw new CommonException("设备不存在，设备id: " + deviceId);
            deviceLoginVO.setErrcode(2);
            return deviceLoginVO;
        }

        // 校验密码
        if(Boolean.FALSE.equals(device.getPassword().equals(deviceLoginDTO.getSecret()))){
//            throw new CommonException("密码错误");
            deviceLoginVO.setErrcode(1);
            return deviceLoginVO;
        }

        // 生成accessToken  refreshToken
        String accessToken = UUID.randomUUID().toString();
        String refreshToken = UUID.randomUUID().toString();

        // 更新到redis中
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + "token:" + deviceId).put("accessToken",accessToken);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + "token:" + deviceId).put("refreshToken",refreshToken);

        // 发送设备在线通知
        deviceOnlineNotice("",deviceId);


        deviceLoginVO.setErrcode(0);
        deviceLoginVO.setTimeout(600);
        deviceLoginVO.setAccessToken(accessToken);
        deviceLoginVO.setRefreshToken(refreshToken);
        deviceLoginVO.setTimeout(600);
        deviceLoginVO.setTimestamp(Long.toString(System.currentTimeMillis()));
        deviceLoginVO.setTimezone("Asia/Shanghai");
        deviceLoginVO.setPeriod("600");

        // todo：登录成功后，开启设备心跳

        return deviceLoginVO;
    }

    // 向APP发送设备在线通知   网关id预留
    private void deviceOnlineNotice( String gatewayId, String devId){
        String topic = "/user/"+ getDeviceUserAccount(devId) + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "deviceStatus");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("devId", devId);
        body.put("gatewayId", gatewayId);
        body.put("status", "online");

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        try {
            mqttApi.sendMqttMessage(topic,message);
        }catch (MqttException e){
            e.printStackTrace();
        }

    }

    @Transactional
    @Override
    public DeviceInfoSyncVO syncDeviceInfo(List<DeviceInfoSyncDTO> deviceInfoSyncDTOList) {
        DeviceInfoSyncVO deviceInfoSyncVO = new DeviceInfoSyncVO();

        if(ObjectUtil.isEmpty(deviceInfoSyncDTOList)){
            //throw new CommonException("设备信息不能为空");
            deviceInfoSyncVO.setErrcode(1);
            return deviceInfoSyncVO;
        }

        for(DeviceInfoSyncDTO deviceInfoSyncDTO : deviceInfoSyncDTOList){
            int errCode = saveDeviceBasicInfo(deviceInfoSyncDTO);
            if(errCode != 0){
                deviceInfoSyncVO.setErrcode(errCode);
                return deviceInfoSyncVO;
            }
        }
        deviceInfoSyncVO.setErrcode(0);
        return deviceInfoSyncVO;
    }

    // 更新设备基本信息到数据库
    @Transactional
    public int saveDeviceBasicInfo(DeviceInfoSyncDTO deviceInfoSyncDTO){
        String deviceId = deviceInfoSyncDTO.getDevId();
        Device device = this.getById(deviceId);

        // 判断设备是否存在
        if(ObjectUtil.isNull(device)){
//            throw new CommonException("设备不存在，设备id: " + deviceId);
            return 2;
        }

        // 更新数据库中，设备的基本信息
        device = BeanUtil.toBean(deviceInfoSyncDTO.getDevInfo(),Device.class);
        device.setId(deviceId);
        device.setModelData(deviceInfoSyncDTO.getServices().toString());
        Boolean updateResult = this.updateById(device);
        if(Boolean.FALSE.equals(updateResult)){
            throw new CommonException("更新设备数据失败");
        }

        return 0;
    }

    @Override
    public DeviceDataVO deviceDataUpload(List<DeviceDataInfoDTO> deviceDataInfoDTOList) {
        DeviceDataVO deviceDataVO = new DeviceDataVO();
        if(ObjectUtil.isEmpty(deviceDataInfoDTOList)){
            deviceDataVO.setErrcode(1);
            return deviceDataVO;
        }

        for(DeviceDataInfoDTO deviceDataInfoDTO : deviceDataInfoDTOList){
            int errCode = storeDeviceDataToCache(deviceDataInfoDTO);
            if(errCode != 0){
                deviceDataVO.setErrcode(errCode);
                return deviceDataVO;
            }
        }

        deviceDataVO.setErrcode(0);
        return deviceDataVO;

    }

    // 存储设备服务数据到缓存中，并发送mqtt通知
    private int storeDeviceDataToCache(DeviceDataInfoDTO deviceDataInfoDTO) {
        int errCode = 0;
        String deviceId = deviceDataInfoDTO.getDevId();
        Device device = this.getById(deviceId);
        if(ObjectUtil.isNull(device)){
            return 2;
        }

        // 保存设备服务数据到缓存中
        JSONArray serviceInfoList = new JSONArray(device.getModelData());
        JSONArray servicesDataList = new JSONArray(deviceDataInfoDTO.getServices());

        for(Object serviceObj : servicesDataList){
            JSONObject serviceDataObj = new JSONObject(serviceObj.toString());
            String sid = serviceDataObj.get("sid").toString();

            for (Object serviceInfoObj : serviceInfoList) {
                JSONObject serviceInfo = new JSONObject(serviceInfoObj.toString());
                if(sid.equals(serviceInfo.get("sid").toString())){
                    serviceDataObj.put("data",serviceInfo.get("data"));
                    serviceDataObj.remove("reportId");
                }

            }

        }

        redisTemplate.boundHashOps(IOT_REDIS_PREFIX +  deviceId).put("modelData",servicesDataList.toString());

        // 发送MQTT设备状态变化通知
        try{
            sendDeviceDataChanged(deviceId,"",servicesDataList);
        }catch(MqttException e){
            e.printStackTrace();
        }

        return errCode;
    }

    // 向APP发送设备数据变化通知
    private void sendDeviceDataChanged(String deviceId,String gatewayId,Object servicesData) throws MqttException {

        String topic = "/user/"+ getDeviceUserAccount(deviceId) + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("requestId", RandomUtil.randomString(8));
        header.put("from", "/na/dataService");
        header.put("to", "/app");
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "deviceDataChanged");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("devId", deviceId);
        body.put("gatewayId", gatewayId);
        body.put("services", servicesData);

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        mqttApi.sendMqttMessage(topic,message);
    }

    private String getDeviceUserAccount(String deviceId){
        return projectDeviceApi.getUserAccountByDeviceId(deviceId);
    }

    @Transactional
    @Override
    public DeviceRevokeVO deviceRevoke(DeviceRevokeDTO deviceRevokeDTO) {
        DeviceRevokeVO deviceRevokeVO = new DeviceRevokeVO();
        String deviceId = deviceRevokeDTO.getDevId();
        if(Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))){
//            throw new CommonException("设备id不存在");
            deviceRevokeVO.setErrcode(1);
            return deviceRevokeVO;
        }

        // 清除Redis中的设备缓存信息
        Boolean clearCache = clearDeviceCache(deviceId);
        if(Boolean.FALSE.equals(clearCache)){
//            throw new CommonException("清除设备缓存信息失败");
            deviceRevokeVO.setErrcode(2);
            return deviceRevokeVO;
        }

        // 清除iot_device表记录
        Boolean clearDevice = this.removeById(deviceId);
        if(Boolean.FALSE.equals(clearDevice)){
//            throw new CommonException("清除设备记录失败");
            deviceRevokeVO.setErrcode(3);
            return deviceRevokeVO;
        }

        // 清除app_project_device记录
        Boolean clearProject = projectDeviceApi.deleteProjectDevice(deviceId);
        if(Boolean.FALSE.equals(clearProject)){
//            throw new CommonException("清除项目设备失败");
            deviceRevokeVO.setErrcode(4);
            return deviceRevokeVO;
        }

        // 向APP发送设备删除通知(网关设备待补充)
        sendDeviceDeleteNotice("",deviceId);


        deviceRevokeVO.setErrcode(0);
        return deviceRevokeVO;
    }

    private void sendDeviceDeleteNotice(String gatewayId,String deviceId){
        String topic = "/user/"+ getDeviceUserAccount(deviceId) + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("requestId", RandomUtil.randomString(8));
        header.put("from", "/na/dataService");
        header.put("to", "/app");
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "deviceDeleted");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("devId", deviceId);
        body.put("gatewayId", gatewayId);

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        try {
            mqttApi.sendMqttMessage(topic,message);
        }catch (MqttException e){
            e.printStackTrace();
        }

    }

    @Override
    public DeviceAuthCodeVO deviceAuthCode(DeviceAuthCodeDTO deviceAuthCodeDTO){
        DeviceAuthCodeVO deviceAuthCodeVO = new DeviceAuthCodeVO();

        String deviceId = deviceAuthCodeDTO.getDevId();
        // 校验设备ID是否合法
        if(Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))){
//            throw new CommonException("设备不存在，设备id: " + deviceId);
            deviceAuthCodeVO.setErrcode(2);
            return deviceAuthCodeVO;
        }

        // 随机生成authCode 和  authCodeId
        String authCode = getRandomCode();
        String authCodeId = getRandomCode();

        // 存储到 Redis中
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("authCode",authCode);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("authCodeId",authCodeId);

        deviceAuthCodeVO.setAuthCode(authCode);
        deviceAuthCodeVO.setAuthCodeId(authCodeId);
        deviceAuthCodeVO.setTimeout(86400);
        deviceAuthCodeVO.setErrcode(0);

        return deviceAuthCodeVO;
    }

    @Override
    public DeviceHeartBeatVO deviceHeartBeat(DeviceHeartBeatDTO deviceHeartBeatDTO) {
        DeviceHeartBeatVO deviceHeartBeatVO = new DeviceHeartBeatVO();
        deviceHeartBeatVO.setTimestamp(Long.toString(System.currentTimeMillis()));
        deviceHeartBeatVO.setTimestamp(getTimezone());
        return deviceHeartBeatVO;
    }

    private String getTimezone(){
        // 获取系统默认时区
        TimeZone timeZone = TimeZone.getDefault();

        // 获取时区ID（例如：America/New_York）
        String id = timeZone.getID();

        // 获取时区的短名称（例如：EST或EDT，但不总是可靠）
        String standardName = timeZone.getDisplayName(false, TimeZone.SHORT, Locale.US);
        String daylightName = timeZone.getDisplayName(true, TimeZone.SHORT, Locale.US);

        // 获取原始偏移量（以秒为单位），然后转换为小时和分钟
        int rawOffset = timeZone.getRawOffset();
        int offsetHours = rawOffset / 3600;
        int offsetMinutes = Math.abs((rawOffset % 3600) / 60);

        // 格式化偏移量（例如：-0500）
        String offsetString = String.format("%+03d%02d", offsetHours, offsetMinutes);

        // 注意：下面的DST规则是非常简化的，并且可能不会总是正确
        // POSIX时区格式中的DST规则通常非常复杂
        // 这里只是为了演示如何构造一个基本的POSIX格式字符串

        // 检查是否处于夏令时
        boolean inDST = timeZone.inDaylightTime(new Date());

        // 构造简化的POSIX时区字符串
        // 例如：EST5EDT（这只是一个非常简化的示例，实际POSIX字符串可能更复杂）
        String posixTimezone = standardName + offsetString +
                (inDST ? daylightName.substring(1) : ""); // 去掉DST名称前的首字母（通常是'D'或'S'）

        // 打印POSIX时区字符串
        // 请注意，这个字符串可能并不完全符合POSIX时区格式的规范
        System.out.println("Simplified POSIX Time Zone: " + posixTimezone);

        return posixTimezone;
    }

    // authCode的长度为32字节，由16 bytes的二进制数据转成16进制字符串而成。
    private String getRandomCode(){
        // 使用SecureRandom生成16字节的随机数据
        byte[] randomBytes = new byte[16];
        new SecureRandom().nextBytes(randomBytes);

        // 将字节数组转换成16进制字符串
        StringBuilder hexString = new StringBuilder(32); // 16字节 * 2字符/字节 = 32字符
        for (byte b : randomBytes) {
            // 将每个字节转换成两位的16进制数并追加到StringBuilder中
            hexString.append(String.format("%02x", b & 0xff));
        }
        // 打印生成的authCode
        return hexString.toString();
    }

    @Override
    public List<DeviceInfoVO> getDeviceInfoList(List<String> deviceIdList) {
        List<Device> deviceList = this.listByIds(deviceIdList);
        List<DeviceInfoVO> deviceInfoVOList = new ArrayList<>();
        for(Device device : deviceList){
            DeviceInfoVO deviceInfoVO = new DeviceInfoVO();
            BeanUtil.copyProperties(device,deviceInfoVO);
            deviceInfoVOList.add(deviceInfoVO);
        }
        return deviceInfoVOList;
    }

    @Override
    public DeviceInfoVO getDeviceInfoById(String deviceId) {
        if(ObjectUtil.isNull(deviceId)){
            throw new CommonException("设备id不能为空");
        }
        Device device = this.getById(deviceId);
        DeviceInfoVO deviceInfoVO = new DeviceInfoVO();
        BeanUtil.copyProperties(device,deviceInfoVO);

        return deviceInfoVO;
    }

    private Boolean clearDeviceCache(String deviceId){
        Boolean result1 = redisTemplate.delete(IOT_REDIS_PREFIX + deviceId);
        Boolean result2 = redisTemplate.delete(IOT_REDIS_PREFIX + "token:" + deviceId);
        return Boolean.TRUE.equals(result1) && Boolean.TRUE.equals(result2);
    }

    public Boolean isDeviceSnExist(String sn){
        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
        deviceQueryWrapper.eq("sn",sn);
        List<Device> deviceList = this.list(deviceQueryWrapper);
        return ObjectUtil.isNotEmpty(deviceList);
    }

    public Boolean isDeviceIdExist(String deviceId){
        Device device = this.getById(deviceId);
        return !Objects.isNull(device);
    }

    public void checkProjectId(String projectId){
        if(ObjectUtil.isNull(projectId)){
            throw new CommonException("项目id不能为空");
        }
        List<AppProjectVO> appProjects = projectApi.getAppProjects();
        List<String> appProjectIds = new ArrayList<>();
        for (AppProjectVO appProjectVO : appProjects) {
            appProjectIds.add(appProjectVO.getId());
        }
        if(!appProjectIds.contains(projectId)){
            throw new CommonException("当前用户下没有该项目，项目id:"+projectId);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(DeviceAddParam deviceAddParam) {
        Device device = BeanUtil.toBean(deviceAddParam, Device.class);
        this.save(device);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(DeviceEditParam deviceEditParam) {
        Device device = this.queryEntity(deviceEditParam.getId());
        BeanUtil.copyProperties(deviceEditParam, device);
        this.updateById(device);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<DeviceIdParam> deviceIdParamList) {
        // 执行删除
        this.removeByIds(CollStreamUtil.toList(deviceIdParamList, DeviceIdParam::getId));
    }

    @Override
    public Device detail(DeviceIdParam deviceIdParam) {
        return this.queryEntity(deviceIdParam.getId());
    }

    @Override
    public Device queryEntity(String id) {
        Device device = this.getById(id);
        if(ObjectUtil.isEmpty(device)) {
            throw new CommonException("设备表不存在，id值为：{}", id);
        }
        return device;
    }
}
